//
//  HCLogServer.m
//  HCToolBox
//
//  Created by Hotacool on 2019/4/15.
//

#import "HCLogServer.h"
#import "GCDWebServer.h"
#import "GCDWebServerDataResponse.h"
#import "GCDWebServerFileResponse.h"
#import "HCConsoleLogger.h"
#import "HCUtilities.h"
#import "HCFileLogger.h"

static NSString *const kHCLogServerRequestLogAll = @"/log";
static NSString *const kHCLogServerRequestLogAfter = @"/logAfter";
static NSString *const kHCLogServerRequestLogDownload = @"/download";
static NSString *const kHCLogServerRequestQueryParam = @"timestamp";
static NSString *const kHCLogServerRequestDownloadParam = @"name";
static NSString *const kHCLogTemplateFileName = @"logTemplate";
static NSString *const kHC404FileName = @"404";
static NSString *const kHCLogDownloadFileName = @"download";
@interface HCLogServer () <GCDWebServerDelegate>
@property (nonatomic, strong) GCDWebServer *webServer;
@end

@implementation HCLogServer

- (instancetype)initWithLogger:(HCConsoleLogger*)logger {
    if (self = [super init]) {
        _logger = logger;
    }
    return self;
}

- (NSURL *)serverURL {
    return [self.webServer serverURL];
}

- (BOOL)isRunning {
    return self.webServer.isRunning;
}

- (void)startServerWithPort:(NSInteger)port {
    if ([self.webServer isRunning]) {
        return;
    }
    [self.webServer startWithPort:port bonjourName:nil];
}

- (void)stopServer {
    [self.webServer stop];
    self.webServer = nil;
}

- (GCDWebServer *)webServer {
    if (!_webServer) {
        _webServer = [[GCDWebServer alloc] init];
        _webServer.delegate = self;
        @weakIt(self);
        [_webServer addDefaultHandlerForMethod:@"GET"
                                  requestClass:[GCDWebServerRequest class]
                                  processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
                                      @strongIt(self);
                                      return [self responseWithRequest:request];
                                  }];
    }
    return _webServer;
}

- (void)webServerDidConnect:(GCDWebServer *)server {
    NSLog(@"%s", __func__);
}

- (void)webServerDidDisconnect:(GCDWebServer *)server {
    NSLog(@"%s", __func__);
}

- (void)webServerDidStart:(GCDWebServer *)server {
    NSLog(@"%s", __func__);
}

- (GCDWebServerResponse*)responseWithRequest:(GCDWebServerRequest*)request {
    GCDWebServerResponse *response = nil;
    NSMutableString* string = [[NSMutableString alloc] init];;
    NSString* path = request.path;
    NSDictionary* query = request.query;
    if ([path isEqualToString:kHCLogServerRequestLogAll]) {//log page
        [self _appendLogRecordsToString:string afterAbsoluteTime:0.0];
        NSDictionary *dic = @{@"PRODUCT":[NSString stringWithCString:getprogname() encoding:NSUTF8StringEncoding]
                              ,@"CONTENT":string};
        NSString *filepath = [[HCUtilities resourcesBundle] pathForResource:kHCLogTemplateFileName ofType:@"html"];
        response = [GCDWebServerDataResponse responseWithHTMLTemplate:filepath variables:dic];
    } else if ([path isEqualToString:kHCLogServerRequestLogAfter] && query[kHCLogServerRequestQueryParam]) {// query new log after timestamp
        double time = [query[kHCLogServerRequestQueryParam] doubleValue];
        [self _appendLogRecordsToString:string afterAbsoluteTime:time];
        response = [GCDWebServerDataResponse responseWithHTML:string];
    } else if ([path isEqualToString:kHCLogServerRequestLogDownload]) {// download log with filename
        NSString *fileName;
        if ((fileName = query[kHCLogServerRequestDownloadParam])) {
            NSString *filePath = [[HCFileLogger logsDirectory] stringByAppendingPathComponent:fileName];
            NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
            if (fileType) {
                if ([fileType isEqualToString:NSFileTypeRegular]) {
                    // always allow ranges in our requests
                    response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
                    [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
                }
            }
        } else {// log list
            [self _appendLogFileToString:string];
            NSDictionary *dic = @{@"PRODUCT":[NSString stringWithCString:getprogname() encoding:NSUTF8StringEncoding]
                                  ,@"CONTENT":string};
            NSString *filePath = [[HCUtilities resourcesBundle] pathForResource:kHCLogDownloadFileName ofType:@"html"];
            response = [GCDWebServerDataResponse responseWithHTMLTemplate:filePath variables:dic];
        }
    } else {// 404
        NSString *filePath = [[HCUtilities resourcesBundle] pathForResource:kHC404FileName ofType:@"html"];
        response = [GCDWebServerDataResponse responseWithHTMLTemplate:filePath variables:nil];
    }
    return response;
}

- (void)_appendLogRecordsToString:(NSMutableString*)string afterAbsoluteTime:(double)time {
    NSArray <DDLogMessage*>*logItems;
    if (time <= 0) {
        logItems = self.logger.logMessages;
    } else {
        NSMutableArray *tmp = [NSMutableArray array];
        [self.logger.logMessages enumerateObjectsUsingBlock:^(DDLogMessage * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            NSTimeInterval interval = [obj.timestamp timeIntervalSince1970];
            if (interval > time) {
                [tmp addObject:obj];
            }
        }];
        logItems = tmp;
    }
    __block double maxTime = time;
    [logItems enumerateObjectsUsingBlock:^(DDLogMessage * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        const char* style = "color: dimgray;";
        NSMutableString *formattedMessage = [[NSMutableString alloc] init];
        [formattedMessage appendFormat:@"<td>%@</td> <td>%@</td> <td>%@</td> <td>%@</td>",obj.timestamp,obj.fileName, obj.function, obj.message];
        [string appendFormat:@"<tr style=\"%s\">%@</tr>", style, formattedMessage];
        NSTimeInterval interval = [obj.timestamp timeIntervalSince1970];
        if (interval > maxTime) {
            maxTime = interval ;
        }
    }];
    [string appendFormat:@"<tr id=\"maxTime\" data-value=\"%f\"></tr>", maxTime];
}

- (void)_appendLogFileToString:(NSMutableString*)string {
    NSArray *fileNames = [HCFileLogger logFiles];
    [fileNames enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        const char* style = "color: dimgray;";
        [string appendFormat:@"<p style=\"%s\"><a href=\"/download?name=%@\">%@</a></p>", style, obj, obj];
    }];
}
@end
